/* file contenente la definizione delle funzioni della classe ixp_graph che si occupano di calcolare 
	le metriche degli IXP e degli AS */
#include "ixp_graph.h"


//by Alessio Casella
int ixp_graph:: calcola_metriche ( int i )
{
	map < int, igraph_t >* graphs = &grafi;
	map < int, map < unsigned int, unsigned int > >* mapping = &nodi_indotti;
	switch ( i ) {
		/* calcolo le metriche dei sottografi ottenuti considerando ogni AS membro come membro 
			normale, senza tenere di conto se uno dei membri e' il route server: */
		/* case 0 commentato perche' gia' compreso nel default
		case 0:
			graphs = &grafi;
			mapping = &nodi_indotti;
			break;
		*/
		/* calcolo le metriche dei sottografi ottenuti togliendo il route server: */
		case 1:		
			graphs = &grafi_nrs;
			mapping = &nodi_indotti_nrs;
			cout<<"Calcolo metriche (grafi senza rs) in corso";
			break;
		/* calcolo le metriche dei sottografi ottenuti costrunendo una clique fra i membri 
			collegati al route server: */
		case 2:
			graphs = &grafi_nrs_clique;
			mapping = &nodi_indotti_nrs;
			cout<<"Calcolo metriche (grafi con full mesh fra i peer del rs) in corso";
			break;
		default:
			i = 0;
			cout<<"Calcolo metriche in corso";
	}
	
	map < int, igraph_t >:: const_iterator it;
	/* identificatore dell'ixp: */
	int ixp_id;
	/* puntatore al sottografo di cui calcore le metriche: */
	igraph_t* sottografo;
	/* chiave della map proprieta_ixp: */
	pair <int, int> graph_id;
	graph_id.second = i;
	
	/* variabili per stampare a schermo */
	int count = 0;
	int resto;

	/* calcolo le metriche per ogni sottografo in graphs: */
	for ( it = graphs->begin(); it != graphs->end(); it++ ) {
		metriche_ixp prop;
		ixp_id = it->first;
		sottografo = &((*graphs)[ixp_id]);
		
		prop.tot_members = membri_ixp[ixp_id].size();
		if ( i != 0 && route_server.find( ixp_id ) != route_server.end() )
			/* escludo il route server dal conteggio dei membri */
			prop.tot_members--;
		prop.public_members = (*mapping)[ixp_id].size();
		if ( degree_metrics( sottografo, (*mapping)[ixp_id], prop.peering_members, prop.max_degree, prop.volume, i, prop.degree_pmf, prop.degree_ccdf ) == -1 )
			return -1;
		prop.peering = igraph_ecount( sottografo );
		prop.avg_degree = (float)prop.volume / (float)prop.public_members;
		prop.hub_dominance = (float)prop.max_degree.first / (float)( prop.public_members - 1 );
		map < unsigned int, unsigned int > edge_ext_as;
		if ( internet_metrics( ixp_id, sottografo, i, (*mapping)[ixp_id], prop, edge_ext_as ) == -1 )
			return -1;
		if ( odf_embed_metrics( sottografo, (*mapping)[ixp_id], edge_ext_as, prop ) == -1 )
			return -1;
		prop.odf = (float)prop.edges_cut / (float)( prop.peering + prop.edges_cut );
		prop.expansion = (float)prop.edges_cut / (float)prop.public_members;
		if ( shell_index_metrics( sottografo, prop.avg_shell_index, prop.max_shell_index,
										prop.min_shell_index, prop.shell_index_pmf ) == -1 )
			return -1;
		pmf_to_ccdf< unsigned int >( &prop.shell_index_pmf, prop.shell_index_ccdf );
		if(link_metrics(prop, ixp_id, sottografo)==-1)
			return -1;
			
		graph_id.first = ixp_id;
		proprieta_ixp[graph_id] = prop;
		
		/* stampa a schermo 3 punti dinamici per mostrare l'attivita' del sistema: */
		resto = count % 4;
		if ( count != 0 )
			cout<<"\b\b\b\b";
		count++;
		switch ( resto ) {
			case 0:
				cout<<"    ";
				break;
			case 1:
				cout<<" .  ";
				break;
			case 2:
				cout<<" .. ";
				break;
			case 3:
				cout<<" ...";
				break;
		}
		cout.flush();
	}

	cout<<endl<<"Calcolate le metriche di ogni IXP e AS"<<endl;
	return 0;
}


//by Alessio Casella
int ixp_graph:: degree_metrics ( igraph_t* g, const map<unsigned int, unsigned int >& m, 
									unsigned int& pm, pair<unsigned int, set<unsigned int> >& md, 
									unsigned int& v, int t, map<unsigned int, float>& dg, map<unsigned int, float>& ccdf_dg )
{
	/* pongo nel vettore res il degree di ogni nodo di g */
	igraph_vector_t res;
	igraph_vector_init( &res, 0 );
	int r = igraph_degree( g, &res, igraph_vss_all(), IGRAPH_IN, IGRAPH_NO_LOOPS);
	if ( r == IGRAPH_EINVVID || r == IGRAPH_EINVMODE ) {
		igraph_vector_destroy( &res );
		return -1;
	}
	

	/* calcolo il valore del degree massimo: */
	md.first = igraph_vector_max( &res );
	
	pm = 0;
	unsigned int len = igraph_vector_size( &res );
	if ( len == 0 ) {
		igraph_vector_destroy( &res );
		return -1;
	}
	for ( unsigned int i = 0; i < len; i++ ) {
		/* calcolo il numero di nodi con degree maggiore o uguale a 1: */
		if ( VECTOR(res)[i] >= 1 )
			pm++;
		/*
		 calcolo pmf degree
		*/
		if(dg.find(VECTOR(res)[i])!=dg.end())
			dg[VECTOR(res)[i]]+=(float)1/(float)len;
		else
			dg[VECTOR(res)[i]]=(float)1/(float)len;	
		
		
		map < unsigned int, unsigned int >::const_iterator it = m.begin();
		/* in (*it).second c'e' l'indice del nodo igraph */
		while ( it != m.end() && (*it).second != i )
			it++;
		if ( it != m.end() ) {
			/* inserisco gli ASN che hanno massimo degree in md.second 
				(in md.first c'e' il degree max): */
			if ( VECTOR(res)[i] == md.first )
				/* in (*it).first c'e' l'ASN associato al nodo */
				md.second.insert((*it).first);
			/* utilizzo il degree di questo AS per calcolare le sue metriche: */
			pair<unsigned int,int> metriche ((*it).first,t);
			if (as_metric.find(metriche)== as_metric.end())
			{
			    as_metric[metriche].member_presence=1;
			    as_metric[metriche].member_connectivity=VECTOR(res)[i];
			    as_metric[metriche].global_degree = -1;
			}
			else
			{
			    as_metric[metriche].member_presence++;
			    as_metric[metriche].member_connectivity+=VECTOR(res)[i];
		    }
		}
		else
			return -1;
	}
	
	/* calcolo il volume: */
	v = igraph_vector_sum( &res );
	
	pmf_to_ccdf<unsigned int>(&dg, ccdf_dg);
	igraph_vector_destroy( &res );
	return 0;
}


//by Alessio Casella
int ixp_graph:: internet_metrics ( int ixp_id, igraph_t* g, int rs, 
									const map<unsigned int, unsigned int >& m, metriche_ixp& met, 
									map < unsigned int, unsigned int >& edge_ext_as )
{
	/* numero di edge che puntano fuori dal sottografo dell'IXP: */
	unsigned int edge_esterni = 0;

	igraph_t* internet =	( rs == 1 ) ?	&global_graph_nrs : 
							( rs == 2 ) ?	&global_graph_nrs_clique : 
							&global_graph;
							
	map < unsigned int, unsigned int >* internet_map =	( rs == 1 ) ?	&global_mapping_nrs : 
														( rs == 2 ) ?	&global_mapping_nrs_clique : 
														&global_mapping;
	
	/* pongo nel vettore deg_ixp il degree di ogni nodo di g */
	igraph_vector_t deg_ixp;
	igraph_vector_init( &deg_ixp, 0 );
	int r = igraph_degree( g, &deg_ixp, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS);
	if ( r == IGRAPH_EINVVID || r == IGRAPH_EINVMODE ) {
		igraph_vector_destroy( &deg_ixp );
		return -1;
	}
	
	/* AS Number del membro: */
	unsigned int asn;
	/* id del nodo che rappresenta il membro nel sottografo indotto: */
	unsigned int node_id_ixp;
	/* id del nodo che rappresenta il membro nel grafo di Internet: */
	unsigned int node_id_global;
	
	/* scorro tutti i membri dell'IXP */
	map < unsigned int, unsigned int >::const_iterator it;
	for ( it = m.begin(); it != m.end(); it++ ) {
		
		asn = it->first;
		node_id_ixp = it->second;
		/* chiave per settare le metriche dell'AS nella map as_metric: */
		pair < unsigned int, int > as_key ( asn, rs );
		
		map < unsigned int, unsigned int >::const_iterator it_global;
		it_global = internet_map->find( asn );
		if ( it_global == internet_map->end() ) {
			/* scarto i membri che non hanno alcuna connessione neanche in Internet dopo aver 
				memorizzato che il suo degree e' 0 (se non lo avevo gia' memorizzato) */
			if ( as_metric[as_key].global_degree == -1 )
				as_metric[as_key].global_degree = 0;
			continue;
		}
		node_id_global = it_global->second;
		
		/* calcolo il degree del membro in Internet */
		igraph_vs_t nodo;
		igraph_vs_1( &nodo, node_id_global );
		igraph_vector_t vet;
		igraph_vector_init( &vet, 0 );
		int r = igraph_degree( internet, &vet, nodo, IGRAPH_ALL, IGRAPH_NO_LOOPS);
		if ( r == IGRAPH_EINVVID || r == IGRAPH_EINVMODE ) {
			igraph_vector_destroy( &vet );
			return -1;
		}
		unsigned int deg_glob = VECTOR(vet)[0];
		igraph_vector_destroy( &vet );
		
		if ( as_metric[as_key].global_degree == -1 )
			/* salvo il degree globale se non lo avevo gia' fatto */
			as_metric[as_key].global_degree = deg_glob;
		/* calcolo il numero di edge esterni del membro considerando la differenza dei degree nel 
			grafo globale e nel sottografo */
		edge_ext_as[asn] = deg_glob - VECTOR(deg_ixp)[node_id_ixp];
		/* aggiungo gli edge esterni al totale dell'IXP */
		edge_esterni += edge_ext_as[asn];
		
	}
	
	/* numero nodi totali: */
	unsigned int nodi_internet = igraph_vcount( internet );
	unsigned int link_internet = igraph_ecount( internet );

	/* numero massimo di connessioni con non membri dell'IXP: */
	unsigned int max_ext =  met.public_members * ( nodi_internet - met.public_members );
	
	met.edges_cut = edge_esterni;
	
	met.conductance = (float)edge_esterni / (float)( 2*met.peering + edge_esterni );
	
	met.cut_ratio = (float)edge_esterni / (float)max_ext;
	/* nel caso max_ext sia 0, anche edge_esterni sara' 0 perche' significa che o l'IXP non ha membri 
		pubblici ( met.public_members == 0 ) o tutti i nodi di Internet sono membri dell'IXP
		( nodi_internet == met.public_members ): in entrambi i casi non e' possibile trovare alcun 
		edge esterno e quindi la divisione sara' 0/0=NaN (Not a Number) */
	
	float app = (float)edge_esterni / (float)( 2*( link_internet - met.peering ) + edge_esterni );
	met.norm_cut = met.conductance + app;
	
	igraph_vector_destroy( &deg_ixp );
	return 0;
}


//by Alessio Casella
int ixp_graph:: odf_embed_metrics ( igraph_t* g, map<unsigned int, unsigned int >& m, 
									const map < unsigned int, unsigned int >& edge_ext_as, 
									metriche_ixp& met )
{
	float odf;
	float min = -1, max = 0, somma_odf = 0;
	unsigned int asn;
	unsigned int edge_ext;
	unsigned int degree;
	/* numero di nodi connessi maggiormente verso l'esterno: */
	unsigned int num_magg_ext = 0;
	
	map < unsigned int, unsigned int >::const_iterator it;
	for ( it = edge_ext_as.begin(); it != edge_ext_as.end(); it++ ) {
		asn = it->first;
		edge_ext = it->second;
		
		/* leggo il degree del nodo asn il cui identificatore igraph si trova in m[asn]: */
		igraph_vs_t nodo;
		igraph_vs_1( &nodo, m[asn] );
		igraph_vector_t res;
		igraph_vector_init( &res, 0 );
		int r = igraph_degree( g, &res, nodo, IGRAPH_ALL, IGRAPH_NO_LOOPS);
		if ( r == IGRAPH_EINVVID || r == IGRAPH_EINVMODE ) {
			igraph_vector_destroy( &res );
			return -1;
		}
		degree = VECTOR(res)[0];
		igraph_vector_destroy( &res );
		
		/* odf del membro asn: */
		odf = (float)edge_ext / (float)( degree + edge_ext );
		met.node_odf[asn] = odf;
		/* aggiorno odf minima: */
		if ( odf < min || min == -1 )
			min = odf;
		/* aggiorno odf massima: */
		if ( odf > max )
			max = odf;
		/* aggiorno la somma delle odf: */
		somma_odf += odf;
		/* aggiorno il numero di nodi connessi maggiormente verso l'esterno: */
		if ( odf > 0.5 )
			num_magg_ext++;
		/* embeddedness del nodo: */
		met.node_embed[asn] = 1 - odf;
	}
	
	if ( edge_ext_as.size() == 0 ) {
	/* setto max e min odf a NaN se il sottografo indotto e' formato da solo nodi che non sono connessi
		neanche nel grafo di Internet (in questo caso edge_ext_as e' vuota) */
		met.min_odf = somma_odf / edge_ext_as.size();
		met.max_odf = somma_odf / edge_ext_as.size();
	}
	else {
		met.min_odf = min;
		met.max_odf = max;
	}
	met.avg_odf = somma_odf / (float)edge_ext_as.size();
	/* avg_odf varra' 0/0 = NaN se il sottografo indotto e' formato da solo nodi che non sono connessi
		neanche nel grafo di Internet (poiche' edge_ext_as e' vuota, quindi size() restituisce 0 e la
		somma rimane 0) */
	met.flake_odf = (float)num_magg_ext / (float)edge_ext_as.size();
	/* flake_odf varra' 0/0 = NaN se il sottografo indotto e' formato da solo nodi che non sono connessi
		neanche nel grafo di Internet (poiche' edge_ext_as e' vuota, quindi size() restituisce 0 e 
		num_magg_ext rimane 0) */
	/* embeddedness minima = complemento a 1 della odf massima: */
	met.min_embed = 1 - met.max_odf;
	/* embeddedness massima = complemento a 1 della odf minima: */
	met.max_embed = 1 - met.min_odf;
	/* embeddedness media = (somma(1-odf_dei_nodi))/num_nodi = (num_nodi-somma_odf)/num_nodi = 
		= 1-avg_odf: */
	met.avg_embed = 1 - met.avg_odf;
	
	return 0;
}


//by Alessio Casella
int ixp_graph:: shell_index_metrics ( igraph_t* g, float& avg, unsigned int& max, unsigned int& min,
								 map<unsigned int, float >& pmf)
{
	/* pongo nel vettore res lo shell index di ogni nodo di g */
	igraph_vector_t res;
	igraph_vector_init( &res, 0 );
	igraph_coreness(g, &res, IGRAPH_ALL);
	
	/* shell index minimo e massimo: */
	igraph_real_t i_min, i_max;
	igraph_vector_minmax( &res, &i_min, &i_max );
	min = i_min;
	max = i_max;
	/* densita' di probabilita' discreta di shell index: */
	unsigned int i;
	for ( i = 0; i <= max; i++ )
		pmf[i] = 0;
	unsigned int len = igraph_vector_size( &res );
	if ( len == 0 ) {
		igraph_vector_destroy( &res );
		return -1;
	}
	unsigned int somma = 0;
	float incremento = (float)1 / (float)len;
	for ( i = 0; i < len; i++ ) {
		pmf[VECTOR(res)[i]] += incremento;
		somma += VECTOR(res)[i];
	}
	/* shell index medio: */
	avg = (float)somma / (float)len;
	
	igraph_vector_destroy( &res );
	return 0;
}


//by Alessio Casella
template < class va_type >
void ixp_graph:: pmf_to_ccdf ( const map < va_type, float >* pmf, map < va_type, float >& ccdf )
{
	float somma = 0;
	typename map < va_type, float >::const_iterator it;
	for ( it = pmf->begin(); it != pmf->end(); it++ ) {
		somma += it->second;
		ccdf[it->first] = 1 - somma;
	}
}


//by Giacomo Tanganelli
int ixp_graph::link_metrics(metriche_ixp &met, int ixp_id, igraph_t *graph){

		igraph_real_t connected;
		igraph_transitivity_avglocal_undirected(graph,&connected);
		/* nel calcolo dell'avg_clustering non vengono considerati i nodi con 0 o 1 solo neighbor:
			per i grafi che hanno solo nodi con 0 o 1 neighbor avg_clustering sara' NaN poiche' non ci 
			sono nodi significativi su cui calcolare il clustering coefficient e poi farne la media */
		met.avg_clustering=(float)connected;
		
		/**
		 *	versione alternativa per il calcolo del clustreing coefficient medio, scritta per 
		 *	verificare che la funzione igraph calcolasse effettivamente il clustering coefficient medio
		 *	(Alessio Casella):
		 **/
		/*
		unsigned int num_nodi = igraph_vcount( graph );
		// vairabile per tenere di conto dei nodi con meno di 2 neighbor:
		unsigned int nodi_esclusi = 0;
		// variabile in cui salvare la somma dei clustering coefficient dei nodi del grafo:
		float somma_clustering = 0;
		for ( unsigned int i = 0; i < num_nodi; i++ ) {
		// per tutti i nodi i del grafo:
			igraph_vector_t neig;
			igraph_vector_init( &neig, 0 );
			// metto in neig gli indici dei nodi adiacenti al nodo i:
			int r = igraph_neighbors( graph, &neig, i, IGRAPH_ALL );
			if ( r == IGRAPH_EINVVID || r == IGRAPH_EINVMODE || r == IGRAPH_ENOMEM ) {
				igraph_vector_destroy( &neig );
				return -1;
			}
			int num_neig = igraph_vector_size( &neig );
			if ( num_neig == 0 || num_neig == 1 ) {
			// escludo il nodo i se ha meno di 2 neighbor:
				nodi_esclusi++;
				continue;
			}
			// calcolo il numero di edge che collegano i neighbor del nodo i:
			unsigned int num_edge_neig = 0;
			for ( int n = 0; n < num_neig-1; n++ )
				for ( int k = n+1; k < num_neig; k++ ) {
					igraph_bool_t res;
					igraph_are_connected( graph, VECTOR(neig)[n], VECTOR(neig)[k], &res );
					if ( res == true )
						num_edge_neig++;
				}
			// sommo il clustering coefficient del nodo i alla somma:
			somma_clustering += (float)num_edge_neig / ( num_neig * (float)(num_neig-1) / 2 );
		}
		// calcolo la media dei clustering coefficient:
		// (se il grafo non ha nodi con almeno 2 neighbor, average_clustering sara' 0/0 = NaN)
		float average_clustering = somma_clustering / (float)( num_nodi - nodi_esclusi );
		if ( DEBUG == 1 ) {
			cout<<"IXP "<<ixp_id<<": "<<endl;
			cout<<"\tnodi: "<<num_nodi<<endl;
			cout<<"\tesclusi: "<<nodi_esclusi<<endl;
			cout<<"\tsomma: "<<somma_clustering<<endl;
			cout<<"\tclustering: "<<average_clustering<<endl;
			cout<<"\tclustering igraph: "<<met.avg_clustering<<endl;
		}
		*/

		int num_membri_ASN_pubblico=igraph_vcount( graph );
		
		igraph_real_t i_density;
		igraph_density( graph, &i_density, false );
		float scaled_density=(float) i_density*num_membri_ASN_pubblico;
		
		met.link_density=(float)i_density;
		met.scaled_link_density=(float)scaled_density;
		met.internal_density=(float)1-met.link_density;
		
		//shortest path lenght

		igraph_matrix_t res;
		igraph_matrix_init(&res,0,0);
		int ret=igraph_shortest_paths(graph, &res, igraph_vss_all(), IGRAPH_ALL );
		if(ret==IGRAPH_ENOMEM || ret==IGRAPH_EINVVID || ret==IGRAPH_EINVMODE)
		{
			igraph_matrix_destroy( &res );
			return -1;
		}
		long int sum=0;
		long int n_row=igraph_matrix_nrow(&res);
		long int n_col=igraph_matrix_ncol(&res);
		long int esclusi=0;
		unsigned int massimo=0;
		for(long int r=0; r<n_row;r++)
		{
			for(long int c=0; c<n_col;c++)
			{
				if(MATRIX(res,r,c)!=IGRAPH_INFINITY && MATRIX(res,r,c)!=0)
				{
					sum+=MATRIX(res,r,c);
					if(massimo<MATRIX(res,r,c) || massimo==0)
						massimo=MATRIX(res,r,c);
				}
				else
					esclusi++;
			}
			
		}
		met.avg_shortest_path_length=(float)sum/(n_row*n_col-esclusi);
		met.diameter=massimo;
		igraph_matrix_destroy( &res );
	return 0;
}


//by Alessio Casella
void ixp_graph:: calcola_metriche_agg ()
{
	map < pair < int, int >, metriche_ixp >::const_iterator it;
	int ixp_id;
	string continente;
	int tipo_grafo;
	for ( it = proprieta_ixp.begin(); it != proprieta_ixp.end(); it++ ) {
	
		ixp_id = it->first.first;
		continente = ixps[ixp_id].continent;
		tipo_grafo = it->first.second;
		pair < string, int > key_prop_agg ( continente, tipo_grafo );
		const metriche_ixp* metriche = &(it->second);
		
		if ( proprieta_ixp_agg.find( key_prop_agg ) == proprieta_ixp_agg.end() )
			/* e' la prima volta che trovo un ixp di questo tipo: inizializzazione */
			init_proprieta_agg( &key_prop_agg, metriche );
		else
			/* aggiorno la somma delle metriche
				(NOTA: prima calcolo la somma e alla fine calcolo la media delle metriche) */
			sum_proprieta_agg( &key_prop_agg, metriche );
	}
	/* calcolo la media delle metriche dividendo ciascun valore della metrica aggregata (pari alla 
		somma delle metriche dei singoli IXP) per il numero di IXP */
	avg_proprieta_agg();
	cout<<"Calcolate le metriche aggregate per continente"<<endl;
}


//by Alessio Casella
void ixp_graph:: init_proprieta_agg ( const pair < string, int >* key_prop_agg, 
													const metriche_ixp* metriche )
{
	proprieta_ixp_agg[*key_prop_agg].num_ixp = 1;
	proprieta_ixp_agg[*key_prop_agg].tot_members = metriche->tot_members;
	proprieta_ixp_agg[*key_prop_agg].public_members = metriche->public_members;
	proprieta_ixp_agg[*key_prop_agg].peering_members = metriche->peering_members;
	proprieta_ixp_agg[*key_prop_agg].peering = metriche->peering;
	proprieta_ixp_agg[*key_prop_agg].edges_cut = metriche->edges_cut;
	proprieta_ixp_agg[*key_prop_agg].avg_degree = metriche->avg_degree;
	
	/* max_degree: devo memorizzare anche gli AS che hanno degree massimo */
	proprieta_ixp_agg[*key_prop_agg].max_degree.first = metriche->max_degree.first;
	set < unsigned int >::const_iterator it_md;
	const set < unsigned int >* as_list = &(metriche->max_degree.second);
	for ( it_md = as_list->begin(); it_md != as_list->end(); it_md++ )
		proprieta_ixp_agg[*key_prop_agg].max_degree.second[*it_md] = 1;
	
	/* NOTA: value != value restituisce true se value == NaN */
	if ( metriche->hub_dominance != metriche->hub_dominance ) {
		proprieta_ixp_agg[*key_prop_agg].hub_dominance.first = 0;
		proprieta_ixp_agg[*key_prop_agg].hub_dominance.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].hub_dominance.first = metriche->hub_dominance;
		proprieta_ixp_agg[*key_prop_agg].hub_dominance.second = 0;
	}
	
	proprieta_ixp_agg[*key_prop_agg].volume = metriche->volume;
	
	if ( metriche->avg_clustering != metriche->avg_clustering ) {
		proprieta_ixp_agg[*key_prop_agg].avg_clustering.first = 0;
		proprieta_ixp_agg[*key_prop_agg].avg_clustering.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].avg_clustering.first = metriche->avg_clustering;
		proprieta_ixp_agg[*key_prop_agg].avg_clustering.second = 0;
	}
	
	/* setto i bin vuoti nella link_density_pmf: */
	const int dec = 2;
	const float incr = pow( 10, -dec );
	for( float val = 0; val <= 1; val += incr ) {
		float approx = floor( val*pow( 10, dec ) + 0.5 ) / pow( 10, dec );
		proprieta_ixp_agg[*key_prop_agg].link_density_pmf[approx] = 0;
	}
	
	if ( metriche->link_density != metriche->link_density ) {
		proprieta_ixp_agg[*key_prop_agg].link_density.first = 0;
		proprieta_ixp_agg[*key_prop_agg].link_density.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].link_density.first = metriche->link_density;
		proprieta_ixp_agg[*key_prop_agg].link_density.second = 0;
		/* per aggiornare la link density pmf prima classifico l'effetivo valore della link density 
			nel bin corrispondente arrotondando il valore alla dec-esima cifra decimale,
			altrimenti, essendo la link density un float, ogni IXP avra' un valore presumibilmente 
			diverso, seppur anche di poco, da tutti gli altri ottenendo cosi' una densita' di 
			probabilita' necessariamente uniforme (ogni valore di link density avrebbe probabilita' di 
			comparire pari a 1/numero_IXP): */
		float approx = floor( metriche->link_density*pow( 10, dec ) + 0.5 ) / pow( 10, dec );
		proprieta_ixp_agg[*key_prop_agg].link_density_pmf[approx]++;
	}
	
	if ( metriche->scaled_link_density != metriche->scaled_link_density ) {
		proprieta_ixp_agg[*key_prop_agg].scaled_link_density.first = 0;
		proprieta_ixp_agg[*key_prop_agg].scaled_link_density.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].scaled_link_density.first = metriche->scaled_link_density;
		proprieta_ixp_agg[*key_prop_agg].scaled_link_density.second = 0;
	}
	
	if ( metriche->internal_density != metriche->internal_density ) {
		proprieta_ixp_agg[*key_prop_agg].internal_density.first = 0;
		proprieta_ixp_agg[*key_prop_agg].internal_density.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].internal_density.first = metriche->internal_density;
		proprieta_ixp_agg[*key_prop_agg].internal_density.second = 0;
	}
	
	if ( metriche->avg_shortest_path_length != metriche->avg_shortest_path_length ) {
		proprieta_ixp_agg[*key_prop_agg].avg_shortest_path_length.first = 0;
		proprieta_ixp_agg[*key_prop_agg].avg_shortest_path_length.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].avg_shortest_path_length.first = metriche->avg_shortest_path_length;
		proprieta_ixp_agg[*key_prop_agg].avg_shortest_path_length.second = 0;
	}
	
	if ( metriche->diameter == 0 ) {
		proprieta_ixp_agg[*key_prop_agg].diameter.first = 0;
		proprieta_ixp_agg[*key_prop_agg].diameter.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].diameter.first = metriche->diameter;
		proprieta_ixp_agg[*key_prop_agg].diameter.second = 0;
	}
	
	if ( metriche->odf != metriche->odf ) {
		proprieta_ixp_agg[*key_prop_agg].odf.first = 0;
		proprieta_ixp_agg[*key_prop_agg].odf.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].odf.first = metriche->odf;
		proprieta_ixp_agg[*key_prop_agg].odf.second = 0;
	}
	
	if ( metriche->conductance != metriche->conductance ) {
		proprieta_ixp_agg[*key_prop_agg].conductance.first = 0;
		proprieta_ixp_agg[*key_prop_agg].conductance.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].conductance.first = metriche->conductance;
		proprieta_ixp_agg[*key_prop_agg].conductance.second = 0;
	}
	
	if ( metriche->min_odf != metriche->min_odf ) {
		proprieta_ixp_agg[*key_prop_agg].min_odf.first = 0;
		proprieta_ixp_agg[*key_prop_agg].min_odf.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].min_odf.first = metriche->min_odf;
		proprieta_ixp_agg[*key_prop_agg].min_odf.second = 0;
	}
	
	if ( metriche->max_odf != metriche->max_odf ) {
		proprieta_ixp_agg[*key_prop_agg].max_odf.first = 0;
		proprieta_ixp_agg[*key_prop_agg].max_odf.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].max_odf.first = metriche->max_odf;
		proprieta_ixp_agg[*key_prop_agg].max_odf.second = 0;
	}
	
	if ( metriche->avg_odf != metriche->avg_odf ) {
		proprieta_ixp_agg[*key_prop_agg].avg_odf.first = 0;
		proprieta_ixp_agg[*key_prop_agg].avg_odf.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].avg_odf.first = metriche->avg_odf;
		proprieta_ixp_agg[*key_prop_agg].avg_odf.second = 0;
	}
	
	if ( metriche->flake_odf != metriche->flake_odf ) {
		proprieta_ixp_agg[*key_prop_agg].flake_odf.first = 0;
		proprieta_ixp_agg[*key_prop_agg].flake_odf.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].flake_odf.first = metriche->flake_odf;
		proprieta_ixp_agg[*key_prop_agg].flake_odf.second = 0;
	}
	
	if ( metriche->cut_ratio != metriche->cut_ratio ) {
		proprieta_ixp_agg[*key_prop_agg].cut_ratio.first = 0;
		proprieta_ixp_agg[*key_prop_agg].cut_ratio.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].cut_ratio.first = metriche->cut_ratio;
		proprieta_ixp_agg[*key_prop_agg].cut_ratio.second = 0;
	}
	
	if ( metriche->norm_cut != metriche->norm_cut ) {
		proprieta_ixp_agg[*key_prop_agg].norm_cut.first = 0;
		proprieta_ixp_agg[*key_prop_agg].norm_cut.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].norm_cut.first = metriche->norm_cut;
		proprieta_ixp_agg[*key_prop_agg].norm_cut.second = 0;
	}
	
	proprieta_ixp_agg[*key_prop_agg].expansion = metriche->expansion;
	proprieta_ixp_agg[*key_prop_agg].avg_shell_index = metriche->avg_shell_index;
	proprieta_ixp_agg[*key_prop_agg].max_shell_index = metriche->max_shell_index;
	proprieta_ixp_agg[*key_prop_agg].min_shell_index = metriche->min_shell_index;
	proprieta_ixp_agg[*key_prop_agg].shell_index_pmf = metriche->shell_index_pmf;
	
	if ( metriche->min_embed != metriche->min_embed ) {
		proprieta_ixp_agg[*key_prop_agg].min_embed.first = 0;
		proprieta_ixp_agg[*key_prop_agg].min_embed.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].min_embed.first = metriche->min_embed;
		proprieta_ixp_agg[*key_prop_agg].min_embed.second = 0;
	}
	
	if ( metriche->max_embed != metriche->max_embed ) {
		proprieta_ixp_agg[*key_prop_agg].max_embed.first = 0;
		proprieta_ixp_agg[*key_prop_agg].max_embed.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].max_embed.first = metriche->max_embed;
		proprieta_ixp_agg[*key_prop_agg].max_embed.second = 0;
	}
	
	if ( metriche->avg_embed != metriche->avg_embed ) {
		proprieta_ixp_agg[*key_prop_agg].avg_embed.first = 0;
		proprieta_ixp_agg[*key_prop_agg].avg_embed.second = 1;
	}
	else {
		proprieta_ixp_agg[*key_prop_agg].avg_embed.first = metriche->avg_embed;
		proprieta_ixp_agg[*key_prop_agg].avg_embed.second = 0;
	}
}


//by Alessio Casella
void ixp_graph:: sum_proprieta_agg ( const pair < string, int >* key_prop_agg, 
													const metriche_ixp* metriche )
{
	proprieta_ixp_agg[*key_prop_agg].num_ixp++;
	proprieta_ixp_agg[*key_prop_agg].tot_members += metriche->tot_members;
	proprieta_ixp_agg[*key_prop_agg].public_members += metriche->public_members;
	proprieta_ixp_agg[*key_prop_agg].peering_members += metriche->peering_members;
	proprieta_ixp_agg[*key_prop_agg].peering += metriche->peering;
	proprieta_ixp_agg[*key_prop_agg].edges_cut += metriche->edges_cut;
	proprieta_ixp_agg[*key_prop_agg].avg_degree += metriche->avg_degree;
	
	/* max_degree: devo memorizzare anche gli AS che hanno degree massimo */
	proprieta_ixp_agg[*key_prop_agg].max_degree.first += metriche->max_degree.first;
	set < unsigned int >::const_iterator it_md;
	const set < unsigned int >* as_list = &(metriche->max_degree.second);
	map < unsigned int, unsigned int >::const_iterator it_as;
	for ( it_md = as_list->begin(); it_md != as_list->end(); it_md++ ) {
		it_as = (proprieta_ixp_agg[*key_prop_agg].max_degree.second).find( *it_md );
		if ( it_as == (proprieta_ixp_agg[*key_prop_agg].max_degree.second).end() )
			proprieta_ixp_agg[*key_prop_agg].max_degree.second[*it_md] = 1;
		else
			proprieta_ixp_agg[*key_prop_agg].max_degree.second[*it_md]++;
	}
	
	/* NOTA: value != value restituisce true se value == NaN */
	if ( metriche->hub_dominance != metriche->hub_dominance )
		proprieta_ixp_agg[*key_prop_agg].hub_dominance.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].hub_dominance.first += metriche->hub_dominance;
	
	proprieta_ixp_agg[*key_prop_agg].volume += metriche->volume;
	
	if ( metriche->avg_clustering != metriche->avg_clustering )
		proprieta_ixp_agg[*key_prop_agg].avg_clustering.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].avg_clustering.first += metriche->avg_clustering;
	
	if ( metriche->link_density != metriche->link_density )
		proprieta_ixp_agg[*key_prop_agg].link_density.second++;
	else {
		proprieta_ixp_agg[*key_prop_agg].link_density.first += metriche->link_density;
		
		map < float, float >::iterator it_ld;
		/* per aggiornare la link density pmf prima classifico l'effetivo valore della link density 
			nel bin corrispondente arrotondando il valore alla dec-esima cifra decimale,
			altrimenti, essendo la link density un float, ogni IXP avra' un valore presumibilmente 
			diverso, seppur anche di poco, da tutti gli altri ottenendo cosi' una densita' di 
			probabilita' necessariamente uniforme (ogni valore di link density avrebbe probabilita' di 
			comparire pari a 1/numero_IXP): */
		const int dec = 2;
		float approx = floor( metriche->link_density*pow( 10, dec ) + 0.5 ) / pow( 10, dec );
		proprieta_ixp_agg[*key_prop_agg].link_density_pmf[approx]++;
	}
	
	if ( metriche->scaled_link_density != metriche->scaled_link_density )
		proprieta_ixp_agg[*key_prop_agg].scaled_link_density.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].scaled_link_density.first += metriche->scaled_link_density;
	
	if ( metriche->internal_density != metriche->internal_density )
		proprieta_ixp_agg[*key_prop_agg].internal_density.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].internal_density.first += metriche->internal_density;
	
	if ( metriche->avg_shortest_path_length != metriche->avg_shortest_path_length )
		proprieta_ixp_agg[*key_prop_agg].avg_shortest_path_length.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].avg_shortest_path_length.first += metriche->avg_shortest_path_length;
	
	if ( metriche->diameter == 0 )
		proprieta_ixp_agg[*key_prop_agg].diameter.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].diameter.first += metriche->diameter;
	
	if ( metriche->odf != metriche->odf )
		proprieta_ixp_agg[*key_prop_agg].odf.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].odf.first += metriche->odf;
	
	if ( metriche->conductance != metriche->conductance )
		proprieta_ixp_agg[*key_prop_agg].conductance.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].conductance.first += metriche->conductance;
	
	if ( metriche->min_odf != metriche->min_odf )
		proprieta_ixp_agg[*key_prop_agg].min_odf.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].min_odf.first += metriche->min_odf;
	
	if ( metriche->max_odf != metriche->max_odf )
		proprieta_ixp_agg[*key_prop_agg].max_odf.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].max_odf.first += metriche->max_odf;
	
	if ( metriche->avg_odf != metriche->avg_odf )
		proprieta_ixp_agg[*key_prop_agg].avg_odf.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].avg_odf.first += metriche->avg_odf;
	
	if ( metriche->flake_odf != metriche->flake_odf )
		proprieta_ixp_agg[*key_prop_agg].flake_odf.second++;
	else 
		proprieta_ixp_agg[*key_prop_agg].flake_odf.first += metriche->flake_odf;
	
	if ( metriche->cut_ratio != metriche->cut_ratio )
		proprieta_ixp_agg[*key_prop_agg].cut_ratio.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].cut_ratio.first += metriche->cut_ratio;
	
	if ( metriche->norm_cut != metriche->norm_cut )
		proprieta_ixp_agg[*key_prop_agg].norm_cut.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].norm_cut.first += metriche->norm_cut;
	
	proprieta_ixp_agg[*key_prop_agg].expansion += metriche->expansion;
	proprieta_ixp_agg[*key_prop_agg].avg_shell_index += metriche->avg_shell_index;
	proprieta_ixp_agg[*key_prop_agg].max_shell_index += metriche->max_shell_index;
	proprieta_ixp_agg[*key_prop_agg].min_shell_index += metriche->min_shell_index;
	
	/* shell_index_pmf */
	map < unsigned int, float >::const_iterator it_sid;
	const map < unsigned int, float >* sid = &(metriche->shell_index_pmf);
	unsigned int value;
	float prob;
	map < unsigned int, float >::const_iterator it_si;
	for ( it_sid = sid->begin(); it_sid != sid->end(); it_sid++ ) {
		value = it_sid->first;
		prob = it_sid->second;
		it_si = (proprieta_ixp_agg[*key_prop_agg].shell_index_pmf).find( value );
		if ( it_si == proprieta_ixp_agg[*key_prop_agg].shell_index_pmf.end() )
			proprieta_ixp_agg[*key_prop_agg].shell_index_pmf[value] = prob;
		else
			proprieta_ixp_agg[*key_prop_agg].shell_index_pmf[value] += prob;
	}
	
	if ( metriche->min_embed != metriche->min_embed )
		proprieta_ixp_agg[*key_prop_agg].min_embed.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].min_embed.first += metriche->min_embed;
	
	if ( metriche->max_embed != metriche->max_embed )
		proprieta_ixp_agg[*key_prop_agg].max_embed.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].max_embed.first += metriche->max_embed;
	
	if ( metriche->avg_embed != metriche->avg_embed )
		proprieta_ixp_agg[*key_prop_agg].avg_embed.second++;
	else
		proprieta_ixp_agg[*key_prop_agg].avg_embed.first += metriche->avg_embed;
	
}


//by Alessio Casella
void ixp_graph:: avg_proprieta_agg ()
{
	map < pair < string, int >, metriche_agg >::const_iterator it;
	pair < string, int > key_prop_agg;
	unsigned int num, num_nan;
	for ( it = proprieta_ixp_agg.begin(); it != proprieta_ixp_agg.end(); it++ ) {
	
		key_prop_agg = it->first;
		num = proprieta_ixp_agg[key_prop_agg].num_ixp;
		
		proprieta_ixp_agg[key_prop_agg].tot_members /= num;
		proprieta_ixp_agg[key_prop_agg].public_members /= num;
		proprieta_ixp_agg[key_prop_agg].peering_members /= num;
		proprieta_ixp_agg[key_prop_agg].peering /= num;
		proprieta_ixp_agg[key_prop_agg].edges_cut /= num;
		proprieta_ixp_agg[key_prop_agg].avg_degree /= num;
		proprieta_ixp_agg[key_prop_agg].max_degree.first /= num;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].hub_dominance.second;
		proprieta_ixp_agg[key_prop_agg].hub_dominance.first /= num - num_nan;
		
		proprieta_ixp_agg[key_prop_agg].volume /= num;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].avg_clustering.second;
		proprieta_ixp_agg[key_prop_agg].avg_clustering.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].link_density.second;
		proprieta_ixp_agg[key_prop_agg].link_density.first /= num - num_nan;
		map < float, float >::iterator it_ldpmf;
		map < float, float >* ldpmf = &(proprieta_ixp_agg[key_prop_agg].link_density_pmf);
		for( it_ldpmf = ldpmf->begin(); it_ldpmf != ldpmf->end(); it_ldpmf++ )
			it_ldpmf->second /= num - num_nan;
		
		/* ricavo la ccdf dalla pmf: */
		pmf_to_ccdf< float >( ldpmf, proprieta_ixp_agg[key_prop_agg].link_density_ccdf );
		
		num_nan = proprieta_ixp_agg[key_prop_agg].scaled_link_density.second;
		proprieta_ixp_agg[key_prop_agg].scaled_link_density.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].internal_density.second;
		proprieta_ixp_agg[key_prop_agg].internal_density.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].avg_shortest_path_length.second;
		proprieta_ixp_agg[key_prop_agg].avg_shortest_path_length.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].diameter.second;
		proprieta_ixp_agg[key_prop_agg].diameter.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].odf.second;
		proprieta_ixp_agg[key_prop_agg].odf.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].conductance.second;
		proprieta_ixp_agg[key_prop_agg].conductance.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].min_odf.second;
		proprieta_ixp_agg[key_prop_agg].min_odf.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].max_odf.second;
		proprieta_ixp_agg[key_prop_agg].max_odf.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].avg_odf.second;
		proprieta_ixp_agg[key_prop_agg].avg_odf.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].flake_odf.second;
		proprieta_ixp_agg[key_prop_agg].flake_odf.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].cut_ratio.second;
		proprieta_ixp_agg[key_prop_agg].cut_ratio.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].norm_cut.second;
		proprieta_ixp_agg[key_prop_agg].norm_cut.first /= num - num_nan;
		
		proprieta_ixp_agg[key_prop_agg].expansion /= num;
		proprieta_ixp_agg[key_prop_agg].avg_shell_index /= num;
		proprieta_ixp_agg[key_prop_agg].max_shell_index /= num;
		proprieta_ixp_agg[key_prop_agg].min_shell_index /= num;
		
		/* shell_index_pmf */
		map < unsigned int, float >::iterator it_sipmf;
		map < unsigned int, float >* sipmf;
		sipmf = &(proprieta_ixp_agg[key_prop_agg].shell_index_pmf);
		for ( it_sipmf = sipmf->begin(); it_sipmf != sipmf->end(); it_sipmf++ )
			proprieta_ixp_agg[key_prop_agg].shell_index_pmf[it_sipmf->first] /= num;
		pmf_to_ccdf< unsigned int >( sipmf, proprieta_ixp_agg[key_prop_agg].shell_index_ccdf );
	
		num_nan = proprieta_ixp_agg[key_prop_agg].min_embed.second;
		proprieta_ixp_agg[key_prop_agg].min_embed.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].max_embed.second;
		proprieta_ixp_agg[key_prop_agg].max_embed.first /= num - num_nan;
		
		num_nan = proprieta_ixp_agg[key_prop_agg].avg_embed.second;
		proprieta_ixp_agg[key_prop_agg].avg_embed.first /= num - num_nan;
	}
}



